#region File Description
//-----------------------------------------------------------------------------
// InputState.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements

using System;
using System.Diagnostics;
using Epitech.Krisk.Kinect.Sensor;
using Epitech.Krisk.Kinect.Sensor.Gestures.Navigation;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

#endregion

namespace Epitech.Krisk.Xna.ScreenManagerLib.ScreenManager
{

    public enum MouseButton
    {
        Left,
        Right,
        Center,
        XButton1,
        XButton2
    }

    public enum SwipeState
    {
        Left,
        Right,
        Top,
        Bottom,
        None
    }

    public enum NavigationState
    {
        Down,
        Up,
        Push,
        None
    }

    /// <summary>
    /// Helper for reading input from keyboard, gamepad, and touch input. This class 
    /// tracks both the current and previous state of the input devices, and implements 
    /// query methods for high level input actions such as "move up through the menu"
    /// or "pause the game".
    /// </summary>
    public class InputState
    {
        #region Fields

        public const int MaxInputs = 4;

        public readonly KeyboardState[] CurrentKeyboardStates;
        public readonly GamePadState[] CurrentGamePadStates;
        public MouseState CurrentMouseState;

        public readonly KeyboardState[] LastKeyboardStates;
        public readonly GamePadState[] LastGamePadStates;
        public MouseState LastMouseState;

        public readonly bool[] GamePadWasConnected;

        public readonly SensorManager SensorManagerInput;

        public NavigationState NavigationState = NavigationState.Down;
        public SwipeState SwipeState = SwipeState.None;
        public bool IsZoom = false;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructs a new input state.
        /// </summary>
        public InputState()
        {
            CurrentKeyboardStates = new KeyboardState[MaxInputs];
            CurrentGamePadStates = new GamePadState[MaxInputs];

            LastKeyboardStates = new KeyboardState[MaxInputs];
            LastGamePadStates = new GamePadState[MaxInputs];

            GamePadWasConnected = new bool[MaxInputs];

            SensorManagerInput = SensorManager.Instance;
            SensorManagerInput.LSwipe.OnSwipeToRight += SwipeRight;
            
            SensorManagerInput.RSwipe.OnSwipeToLeft += SwipeLeft;
            SensorManagerInput.RSwipe.OnSwipeToBottom += SwipeBottom;
            SensorManagerInput.RSwipe.OnSwipeToTop += SwipeTop;
            SensorManagerInput.MouseClick += MouseClick;
            //SensorManagerInput.ZoomManager.ZoomEvent += ZoomInput;
        }

        public bool KinectClick { get; set; }

        private void MouseClick(object sender, EventArgs e)
        {
            _lastClick = DateTime.Now;
            KinectClick = true;
        }

        private void SwipeTop(object sender, EventArgs e)
        {
            _lastSwipe = DateTime.Now;
            SwipeState = SwipeState.Top;
        }

        private void SwipeBottom(object sender, EventArgs e)
        {
            _lastSwipe = DateTime.Now;
            SwipeState = SwipeState.Bottom;
        }

        private DateTime _lastZoom;

        private void ZoomInput(double obj)
        {
            IsZoom = true;
            _lastZoom = DateTime.Now;
        }

        private DateTime _lastSwipe;

        private void SwipeRight(object sender, EventArgs e)
        {
            _lastSwipe = DateTime.Now;
            SwipeState = SwipeState.Right;
        }

        private void SwipeLeft(object sender, EventArgs e)
        {
            _lastSwipe = DateTime.Now;
            SwipeState = SwipeState.Left;
        }

        private void NavigationEvent(object sender, SelectionEventArgs e)
        {
            //if (e.Position > 2)
            //    NavigationState = NavigationState.Up;
            //else if (e.Position < 0)
            //    NavigationState = NavigationState.Down;
            //else
            //    NavigationState = NavigationState.None;
            //Debug.WriteLine(e.Position.ToString());
        }   

        #endregion

        #region Public Methods


        /// <summary>
        /// Reads the latest state of the keyboard and gamepad.
        /// </summary>
        public void Update()
        {
            LastMouseState = CurrentMouseState;

            CurrentMouseState = Mouse.GetState();



            for (int i = 0; i < MaxInputs; i++)
            {
                LastKeyboardStates[i] = CurrentKeyboardStates[i];
                LastGamePadStates[i] = CurrentGamePadStates[i];

                CurrentKeyboardStates[i] = Keyboard.GetState((PlayerIndex)i);
                CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);

                // Keep track of whether a gamepad has ever been
                // connected, so we can detect if it is unplugged.
                if (CurrentGamePadStates[i].IsConnected)
                {
                    GamePadWasConnected[i] = true;
                }
            }

            if (DateTime.Now - _lastZoom > TimeSpan.FromSeconds(1))
                IsZoom = false;
            if (DateTime.Now - _lastSwipe > TimeSpan.FromSeconds(1))
                KinectClick = false;
            if (DateTime.Now - _lastSwipe > TimeSpan.FromSeconds(1))
                SwipeState = SwipeState.None;
        }

        public Vector2 MousePosition()
        {
            Vector2 v = CurrentGamePadStates[0].ThumbSticks.Left;
            Mouse.SetPosition(CurrentMouseState.X + (int)(v.X * 20), CurrentMouseState.Y - (int)(v.Y * 20));

            return new Vector2(CurrentMouseState.X, CurrentMouseState.Y);
        }

        public int MouseScrollsValue()
        {
            return CurrentMouseState.ScrollWheelValue;
        }

        public bool IsMouseButtonDown(MouseButton button)
        {
            ButtonState but = ButtonState.Released;

            switch (button)
            {
                case MouseButton.Left:
                        but = CurrentMouseState.LeftButton;
                    break;
                case MouseButton.Right:
                        but = CurrentMouseState.RightButton;
                    break;
                case MouseButton.Center:
                        but = CurrentMouseState.MiddleButton;
                    break;
                case MouseButton.XButton1:
                        but = CurrentMouseState.XButton1;
                    break;
                case MouseButton.XButton2:
                        but = CurrentMouseState.XButton2;
                    break;
                default:
                    break;
            }
            return but == ButtonState.Pressed;
        }

        public bool IsNewMouseButtonDown(MouseButton button)
        {
            ButtonState but = ButtonState.Released;

            switch (button)
            {
                case MouseButton.Left:
                    if (LastMouseState.LeftButton == ButtonState.Released)
                        but = CurrentMouseState.LeftButton;
                    break;
                case MouseButton.Right:
                    if (LastMouseState.RightButton == ButtonState.Released)
                        but = CurrentMouseState.RightButton;
                    break;
                case MouseButton.Center:
                    if (LastMouseState.MiddleButton == ButtonState.Released)
                        but = CurrentMouseState.MiddleButton;
                    break;
                case MouseButton.XButton1:
                    if (LastMouseState.XButton1 == ButtonState.Released)
                        but = CurrentMouseState.XButton1;
                    break;
                case MouseButton.XButton2:
                    if (LastMouseState.XButton2 == ButtonState.Released)
                        but = CurrentMouseState.XButton2;
                    break;
                default:
                    break;
            }
            return but == ButtonState.Pressed;
        }

        /// <summary>
        /// Helper for checking if a key was newly pressed during this update. The
        /// controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When a keypress
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsNewKeyPress(Keys key, PlayerIndex? controllingPlayer,
                                            out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                // Read input from the specified player.
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (CurrentKeyboardStates[i].IsKeyDown(key) &&
                        LastKeyboardStates[i].IsKeyUp(key));
            }
            else
            {
                // Accept input from any player.
                return (IsNewKeyPress(key, PlayerIndex.One, out playerIndex) ||
                        IsNewKeyPress(key, PlayerIndex.Two, out playerIndex) ||
                        IsNewKeyPress(key, PlayerIndex.Three, out playerIndex) ||
                        IsNewKeyPress(key, PlayerIndex.Four, out playerIndex));
            }
        }

        private DateTime _lastNavigation = DateTime.Now;
        private DateTime _lastClick;

        public bool IsKinectNaviationActive(NavigationState state)
        {
            if (DateTime.Now - _lastNavigation < TimeSpan.FromSeconds(0.25))
                return false;
            _lastNavigation = DateTime.Now;
            if (state == NavigationState.Down && state == NavigationState)
                return true; 
            if (NavigationState == state)
                return true;
            return false;
        }

        /// <summary>
        /// Helper for checking if a key was newly or hold pressed during this update. The
        /// controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When a keypress
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsKeyPress(Keys key, PlayerIndex? controllingPlayer,
                                            out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                // Read input from the specified player.
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (CurrentKeyboardStates[i].IsKeyDown(key));
            }
            else
            {
                // Accept input from any player.
                return (IsKeyPress(key, PlayerIndex.One, out playerIndex) ||
                        IsKeyPress(key, PlayerIndex.Two, out playerIndex) ||
                        IsKeyPress(key, PlayerIndex.Three, out playerIndex) ||
                        IsKeyPress(key, PlayerIndex.Four, out playerIndex));
            }
        }


        /// <summary>
        /// Helper for checking if a button was newly pressed during this update.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When a button press
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsNewButtonPress(Buttons button, PlayerIndex? controllingPlayer,
                                                     out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                // Read input from the specified player.
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (CurrentGamePadStates[i].IsButtonDown(button) &&
                        LastGamePadStates[i].IsButtonUp(button));
            }
            else
            {
                // Accept input from any player.
                return (IsNewButtonPress(button, PlayerIndex.One, out playerIndex) ||
                        IsNewButtonPress(button, PlayerIndex.Two, out playerIndex) ||
                        IsNewButtonPress(button, PlayerIndex.Three, out playerIndex) ||
                        IsNewButtonPress(button, PlayerIndex.Four, out playerIndex));
            }
        }


        public bool IsButtonPress(Buttons button, PlayerIndex? controllingPlayer,
                                             out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                // Read input from the specified player.
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (CurrentGamePadStates[i].IsButtonDown(button));
            }
            else
            {
                // Accept input from any player.
                return (IsButtonPress(button, PlayerIndex.One, out playerIndex) ||
                        IsButtonPress(button, PlayerIndex.Two, out playerIndex) ||
                        IsButtonPress(button, PlayerIndex.Three, out playerIndex) ||
                        IsButtonPress(button, PlayerIndex.Four, out playerIndex));
            }
        }


        /// <summary>
        /// Checks for a "menu select" input action.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When the action
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsMenuSelect(PlayerIndex? controllingPlayer,
                                 out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Space, controllingPlayer, out playerIndex) ||
                   IsNewKeyPress(Keys.Enter, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.A, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex) ||
                   KinectClick;
        }


        /// <summary>
        /// Checks for a "menu cancel" input action.
        /// The controllingPlayer parameter specifies which player to read input for.
        /// If this is null, it will accept input from any player. When the action
        /// is detected, the output playerIndex reports which player pressed it.
        /// </summary>
        public bool IsMenuCancel(PlayerIndex? controllingPlayer,
                                 out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.B, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex);
        }


        /// <summary>
        /// Checks for a "menu up" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsMenuUp(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Up, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.DPadUp, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.LeftThumbstickUp, controllingPlayer, out playerIndex);
        }

        /// <summary>
        /// Checks for a "menu down" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsMenuDown(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Down, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.DPadDown, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.LeftThumbstickDown, controllingPlayer, out playerIndex);
        }


        /// <summary>
        /// Checks for a "pause the game" input action.
        /// The controllingPlayer parameter specifies which player to read
        /// input for. If this is null, it will accept input from any player.
        /// </summary>
        public bool IsPauseGame(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex) ||
                   IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
        }

        public Keys[] GetPressedKeys()
        {
            return CurrentKeyboardStates[0].GetPressedKeys();
        }

        #endregion
    }
}
